#include "a7680c_at_modem.h"
#include <cstring>
#include <esp_err.h>
#include <esp_log.h>
#include <iomanip>
#include <sstream>

static const char *TAG = "A7680cAtModem";

/**
 * @brief 检查字符串是否仅包含数字且长度小于10
 *
 * @param s 待检查的字符串
 * @return true 字符串是纯数字且长度合法
 * @return false 字符串为空/包含非数字字符/长度超过限制
 */
static bool is_number(const std::string &s) {
  return !s.empty() && std::all_of(s.begin(), s.end(), ::isdigit) && s.length() < 10;
}

A7680cAtModem::A7680cAtModem(int tx_pin, int rx_pin, size_t rx_buffer_size)
    : rx_buffer_size_(rx_buffer_size), uart_num_(DEFAULT_UART_NUM), tx_pin_(tx_pin), rx_pin_(rx_pin),
      baud_rate_(DEFAULT_BAUD_RATE) {
  event_group_handle_ = xEventGroupCreate();

  uart_config_t uart_config = {};
  uart_config.baud_rate     = baud_rate_;
  uart_config.data_bits     = UART_DATA_8_BITS;
  uart_config.parity        = UART_PARITY_DISABLE;
  uart_config.stop_bits     = UART_STOP_BITS_1;
  uart_config.source_clk    = UART_SCLK_DEFAULT;

  ESP_ERROR_CHECK(uart_driver_install(uart_num_, rx_buffer_size_ * 2, 0, 100, &event_queue_handle_, 0));
  ESP_ERROR_CHECK(uart_param_config(uart_num_, &uart_config));
  ESP_ERROR_CHECK(uart_set_pin(uart_num_, tx_pin_, rx_pin_, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));

  xTaskCreate(
      [](void *arg) {
        auto a7680c_at_modem = (A7680cAtModem *)arg;
        a7680c_at_modem->EventTask();
        vTaskDelete(NULL);
      },
      "modem_event",
      4096,
      this,
      15,
      &event_task_handle_);

  xTaskCreate(
      [](void *arg) {
        auto a7680c_at_modem = (A7680cAtModem *)arg;
        a7680c_at_modem->ReceiveTask();
        vTaskDelete(NULL);
      },
      "modem_receive",
      4096 * 2,
      this,
      15,
      &receive_task_handle_);
}

A7680cAtModem::~A7680cAtModem() {
  vTaskDelete(event_task_handle_);
  vTaskDelete(receive_task_handle_);
  vEventGroupDelete(event_group_handle_);
  uart_driver_delete(uart_num_);
}

/**
 * @brief 发送AT命令并等待响应
 *
 * @param command 要发送的AT命令字符串
 * @param timeout_ms 等待响应的超时时间(毫秒)，0表示不等待
 * @return true 命令发送成功且收到正确响应
 * @return false 命令发送失败或收到错误响应或超时
 *
 * @note 该函数是线程安全的，内部使用了互斥锁保护
 * @note 当debug_为true时会打印发送的命令日志
 */
bool A7680cAtModem::Command(const std::string command, int timeout_ms) {
  std::lock_guard<std::mutex> lock(command_mutex_);
  if (debug_) {
    ESP_LOGI(TAG, ">> %.64s", command.c_str());
  }
  response_.clear();
  {
    std::lock_guard<std::mutex> lock(mutex_);
    last_command_ = command + "\r\n";
    int ret       = uart_write_bytes(uart_num_, last_command_.c_str(), last_command_.length());
    if (ret < 0) {
      ESP_LOGE(TAG, "uart_write_bytes failed: %d", ret);
      return false;
    }
  }

  if (timeout_ms > 0) {
    auto bits = xEventGroupWaitBits(
        event_group_handle_,
        AT_EVENT_COMMAND_DONE | AT_EVENT_COMMAND_ERROR,
        pdTRUE,
        pdFALSE,
        pdMS_TO_TICKS(timeout_ms));
    if (bits & AT_EVENT_COMMAND_DONE) {
      ESP_LOGI(TAG, "AT_EVENT_COMMAND_DONE");
      return true;
    } else if (bits & AT_EVENT_COMMAND_ERROR) {
      ESP_LOGE(TAG, "command error: %s", command.c_str());
      return false;
    }
  }
  return false;
}

bool A7680cAtModem::DetectBaudRate() {
  // Write and Read AT command to detect the current baud rate
  std::vector<int> baud_rates = {115200, 921600, 460800, 230400, 57600, 38400, 19200, 9600};
  while (true) {
    ESP_LOGI(TAG, "Detecting baud rate...");
    for (int rate : baud_rates) {
      uart_set_baudrate(uart_num_, rate);
      if (Command("AT", 20)) {
        ESP_LOGI(TAG, "Detected baud rate: %d", rate);
        baud_rate_ = rate;
        return true;
      }
    }
    vTaskDelay(pdMS_TO_TICKS(1000));
  }
  return false;
}

bool A7680cAtModem::SetBaudRate(int new_baud_rate) {
  if (!DetectBaudRate()) {
    ESP_LOGE(TAG, "Failed to detect baud rate");
    return false;
  }
  if (new_baud_rate == baud_rate_) {
    return true;
  }

  // Set new baud rate
  if (Command(std::string("AT+IPR=") + std::to_string(new_baud_rate))) {
    uart_set_baudrate(uart_num_, new_baud_rate);
    baud_rate_ = new_baud_rate;
    ESP_LOGI(TAG, "Set baud rate to %d", new_baud_rate);
    return true;
  }
  ESP_LOGI(TAG, "Failed to set baud rate to %d", new_baud_rate);
  return false;
}

int A7680cAtModem::WaitForNetworkReady() {

  ESP_LOGI(TAG, "Waiting for network ready...");
  Command("AT+CEREG=1", 1000);
  while (!network_ready_) {
    if (pin_ready_ == 2) {
      ESP_LOGE(TAG, "PIN is not ready");
      return -1;
    }
    if (registration_state_ == 3) {
      ESP_LOGI(TAG, "Registration denied");
      return -2;
    }
    Command("AT+MIPCALL?");
    xEventGroupWaitBits(event_group_handle_, AT_EVENT_NETWORK_READY, pdTRUE, pdFALSE, pdMS_TO_TICKS(1000));
  }
  return 0;
}

void A7680cAtModem::EventTask() {
  uart_event_t event;
  while (true) {
    if (xQueueReceive(event_queue_handle_, &event, portMAX_DELAY) == pdTRUE) {
      switch (event.type) {
      case UART_DATA:
        xEventGroupSetBits(event_group_handle_, AT_EVENT_DATA_AVAILABLE);
        // ESP_LOGI(TAG, "data available");
        break;
      case UART_BREAK:
        ESP_LOGI(TAG, "break");
        break;
      case UART_BUFFER_FULL:
        ESP_LOGE(TAG, "buffer full");
        break;
      case UART_FIFO_OVF:
        ESP_LOGE(TAG, "FIFO overflow");
        NotifyCommandResponse("FIFO_OVERFLOW", {});
        break;
      default:
        ESP_LOGE(TAG, "unknown event type: %d", event.type);
        break;
      }
    }
  }
}

void A7680cAtModem::ReceiveTask() {
  while (true) {
    auto bits = xEventGroupWaitBits(
        event_group_handle_,
        AT_EVENT_DATA_AVAILABLE,
        pdTRUE,       // 自动清除该位
        pdFALSE,      // 任意一位满足即可（不是等所有位）
        portMAX_DELAY // 无限等待
    );

    if (bits & AT_EVENT_DATA_AVAILABLE) {
      size_t available;

      // 获取 UART 驱动缓冲区中当前可读取的数据字节数。
      uart_get_buffered_data_len(uart_num_, &available);
      ESP_LOGI(TAG, "available: %d", available);
      if (available > 0) {
        // Extend rx_buffer_ and read into buffer
        rx_buffer_.resize(rx_buffer_.size() + available);
        char *rx_buffer_ptr = &rx_buffer_[rx_buffer_.size() - available];
        // 调用 uart_read_bytes() 会自动将数据从 UART 驱动缓冲区中删除（取出），你不需要额外删除，也不能重复读取。
        uart_read_bytes(uart_num_, rx_buffer_ptr, available, portMAX_DELAY);
        while (ParseResponse()) {
        }
      }
    }
  }
}

/**
 * @brief 解析AT命令响应
 *
 * 该方法用于解析从A7680c模块接收到的AT命令响应。它会处理以下几种情况：
 * 1. 以"+"开头的扩展错误响应（如"+CME ERROR"）
 * 2. "OK"响应
 * 3. ">"提示符
 * 4. "ERROR"响应
 * 5. 其他普通响应
 *
 * 解析成功后会将响应内容存入response_或通过NotifyCommandResponse通知，
 * 并设置相应的事件标志位。
 *
 * @return true - 解析成功
 * @return false - 解析失败（如缓冲区中没有完整响应）
 */
bool A7680cAtModem::ParseResponse() {

  // 如果没有 \r\n，说明还没接收完整，等待下一波数据。
  auto end_pos = rx_buffer_.find("\r\n");
  if (end_pos == std::string::npos) {
    return false;
  }

  // 这表示 \r\n 就在最开始，说明当前行是空行（也就是一行只有回车换行，没有内容）
  if (end_pos == 0) {
    rx_buffer_.erase(0, 2);
    return true;
  }
  if (debug_) {
    ESP_LOGI(TAG, "<< %.64s", rx_buffer_.substr(0, end_pos).c_str());
    // print last 64 bytes before end_pos if available
    // if (end_pos > 64) {
    //     ESP_LOGI(TAG, "<< LAST: %.64s", rx_buffer_.c_str() + end_pos - 64);
    // }
  }

  // Parse "+CME ERROR: 123,456,789"
  if (rx_buffer_[0] == '+') {
    std::string command, values;
    auto        pos = rx_buffer_.find(": ");
    if (pos == std::string::npos || pos > end_pos) {
      command = rx_buffer_.substr(1, end_pos - 1); // 没有值
    } else {
      command = rx_buffer_.substr(1, pos - 1);                 // 提取命令
      values  = rx_buffer_.substr(pos + 2, end_pos - pos - 2); // 提取参数
    }
    rx_buffer_.erase(0, end_pos + 2);

    // Parse "string", int, int, ... into AtArgumentValue
    std::vector<AtArgumentValue> arguments;
    std::istringstream           iss(values);
    std::string                  item;
    while (std::getline(iss, item, ',')) {
      AtArgumentValue argument;
      if (item.front() == '"') {
        argument.type         = AtArgumentValue::Type::String;
        argument.string_value = item.substr(1, item.size() - 2);
      } else if (item.find(".") != std::string::npos) {
        argument.type         = AtArgumentValue::Type::Double;
        argument.double_value = std::stod(item);
      } else if (is_number(item)) {
        argument.type         = AtArgumentValue::Type::Int;
        argument.int_value    = std::stoi(item);
        argument.string_value = std::move(item);
      } else {
        argument.type         = AtArgumentValue::Type::String;
        argument.string_value = std::move(item);
      }
      arguments.push_back(argument);
    }

    // NotifyCommandResponse(command, arguments);
    return true;
  } else if (/* OK\r\n */
      rx_buffer_.size() >= 4 && rx_buffer_[0] == 'O' && rx_buffer_[1] == 'K' && rx_buffer_[2] == '\r' &&
      rx_buffer_[3] == '\n') {
    rx_buffer_.erase(0, 4);
    xEventGroupSetBits(event_group_handle_, AT_EVENT_COMMAND_DONE);
    return true;
  } else if (rx_buffer_.size() >= 1 && rx_buffer_[0] == '>') {
    rx_buffer_.erase(0, 1);
    xEventGroupSetBits(event_group_handle_, AT_EVENT_COMMAND_DONE);
    return true;
  } else if (/* ERROR\r\n */
      rx_buffer_.size() >= 7 && rx_buffer_[0] == 'E' && rx_buffer_[1] == 'R' && rx_buffer_[2] == 'R' &&
      rx_buffer_[3] == 'O' && rx_buffer_[4] == 'R' && rx_buffer_[5] == '\r' && rx_buffer_[6] == '\n') {
    rx_buffer_.erase(0, 7);
    xEventGroupSetBits(event_group_handle_, AT_EVENT_COMMAND_ERROR);
    return true;
  } else {
    response_ = rx_buffer_.substr(0, end_pos);
    rx_buffer_.erase(0, end_pos + 2);
    return true;
  }
  return false;
}

void A7680cAtModem::NotifyCommandResponse(const std::string &command, const std::vector<AtArgumentValue> &arguments) {

  if (command == "CME ERROR") {
    xEventGroupSetBits(event_group_handle_, AT_EVENT_COMMAND_ERROR);
    return;
  }
  if (command == "MIPCALL" && arguments.size() >= 3) {
    if (arguments[1].int_value == 1) {
      ip_address_    = arguments[2].string_value;
      network_ready_ = true;
      xEventGroupSetBits(event_group_handle_, AT_EVENT_NETWORK_READY);
    }
  } else if (command == "ICCID" && arguments.size() >= 1) {
    iccid_ = arguments[0].string_value;
  } else if (command == "COPS" && arguments.size() >= 4) {
    carrier_name_ = arguments[2].string_value;
  } else if (command == "CSQ" && arguments.size() >= 1) {
    csq_ = arguments[0].int_value;
  } else if (command == "MATREADY") {
    network_ready_ = false;
    if (on_material_ready_) {
      on_material_ready_();
    }
  } else if (command == "CEREG" && arguments.size() >= 1) {
    if (arguments.size() == 1) {
      registration_state_ = arguments[0].int_value;
    } else {
      registration_state_ = arguments[1].int_value;
    }
  } else if (command == "CPIN" && arguments.size() >= 1) {
    if (arguments[0].string_value == "READY") {
      pin_ready_ = 1;
    } else {
      pin_ready_ = 2;
    }
  }

  std::lock_guard<std::mutex> lock(mutex_);
  for (auto &callback : on_data_received_) {
    callback(command, arguments);
  }
}

/* *************************END OF FILE************************* */
